Skip to content

Add Node.js agent templates and unlock JavaScript in agent builder#3374

Open
mich-elle-luna wants to merge 4 commits into
mainfrom
agent-builder-node-js
Open

Add Node.js agent templates and unlock JavaScript in agent builder#3374
mich-elle-luna wants to merge 4 commits into
mainfrom
agent-builder-node-js

Conversation

@mich-elle-luna

@mich-elle-luna mich-elle-luna commented May 27, 2026

Copy link
Copy Markdown
Collaborator

Add conversational and recommendation agent templates using node-redis v4 with Redis Search for vector-based message history and movie indexing. Conversational agent uses hybrid recent+semantic context retrieval, runtime embedding dimension validation, and a clear note on Redis version requirements. Recommendation agent parses genres from MovieLens CSV format into Redis TAGs, validates LLM query params against an explicit allowlist, skips dataset reload if the index is already warm, and filters genres in Redis rather than JS. Fix template URL to use root-relative path so local templates load in dev. Update agent builder to support JavaScript alongside Python.


Note

Low Risk
Documentation and static client-side templates only; no server auth or production runtime changes in this repo.

Overview
JavaScript (Node.js) is now a fully supported language in the AI agent builder, alongside Python. Docs list Python and JavaScript as available; Java and C# remain coming soon.

New Node.js templates ship for conversational and recommendation agents: node-redis with Redis Search—JSON vector indexes and hybrid recent + semantic context for chat; MovieLens ingest, LLM-parsed filters with allowlisted params, and genre matching via Redis TAGs for recommendations.

Agent builder UI/logic treats JavaScript like Python in the wizard, filters model choices to exclude Anthropic for JS (OpenAI SDK only), loads templates via a root-relative /code/agent-templates/... path, updates the generic JS stub for node-redis v4 env vars, and keeps the Jupyter button disabled for all outputs.

Reviewed by Cursor Bugbot for commit ec2044f. Bugbot is set up for automated code reviews on this repo. Configure here.

Add conversational and recommendation agent templates using node-redis v4
with Redis Search for vector-based message history and movie indexing.
Conversational agent uses hybrid recent+semantic context retrieval, runtime
embedding dimension validation, and a clear note on Redis version requirements.
Recommendation agent parses genres from MovieLens CSV format into Redis TAGs,
validates LLM query params against an explicit allowlist, skips dataset reload
if the index is already warm, and filters genres in Redis rather than JS.
Fix template URL to use root-relative path so local templates load in dev.
Update agent builder to support JavaScript alongside Python.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

@mich-elle-luna mich-elle-luna requested a review from a team May 27, 2026 20:22
@jit-ci

jit-ci Bot commented May 27, 2026

Copy link
Copy Markdown

🛡️ Jit Security Scan Results

CRITICAL HIGH MEDIUM

✅ No security findings were detected in this PR


Security scan by Jit

Comment thread static/code/agent-templates/javascript/conversational_agent.js
Comment thread static/code/agent-templates/javascript/conversational_agent.js Outdated
Comment thread static/js/agent-builder.js

@dwdougherty dwdougherty left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, but I think somebody with more Node.js experience should take a look. I'll add Docs back into the review fray.

@dwdougherty dwdougherty requested a review from a team May 27, 2026 21:13
@andy-stark-redis

Copy link
Copy Markdown
Contributor

I got Claude to have a look and he found the following:

Agent builder — JavaScript generation review

Findings from auditing the JavaScript code generated by the agent builder on the Agent builder page. Templates live in static/code/agent-templates/javascript/; the substitution logic is agent-builder.js:529-560.

What was checked: syntax of all 6 generated outputs (2 agent types × 3 models), template substitution correctness, Python ↔ JS parity, and runtime behaviour against Redis 8.2.1 (RediSearch + ReJSON loaded) with the OpenAI SDK stubbed.

What was not checked: real LLM calls, the Anthropic and Llama3 variants end-to-end, the full MovieLens dataset (used a 5-movie synthetic seed), or non-OpenAI embedding behaviour.

Critical — blocks fresh installs

1. ft.dropIndex error-string match is obsolete

recommendation_agent.js:210-215

try {
  await this.redisClient.ft.dropIndex(INDEX_NAME);
} catch (err) {
  if (!err.message?.includes('Unknown Index name')) throw err;
}

Current Redis (verified on 8.2.1 via redis-cli FT.DROPINDEX nonexistent_idx) returns "<name>: no such index", not "Unknown Index name". On a fresh install where movies_idx doesn't exist yet, the catch re-throws and the agent crashes during connect() before any data is loaded.

Fix sketch: match the modern wording too, or replace the string check with a structural test:

const exists = await this.redisClient.ft.info(INDEX_NAME).then(() => true).catch(() => false);
if (exists) await this.redisClient.ft.dropIndex(INDEX_NAME);

2. info.numDocs should be info.num_docs

recommendation_agent.js:133-140

async _indexExists() {
  try {
    const info = await this.redisClient.ft.info(INDEX_NAME);
    return parseInt(info.numDocs) > 0;
  } catch { return false; }
}

ft.info() in node-redis returns the field as num_docs (server-native, snake_case). parseInt(undefined) > 0 is always false, so _indexExists() always returns false. The "skip dataset import if already loaded" shortcut in _setupMovieIndex() never fires — every startup reloads the entire MovieLens dataset and rewrites every key, including dropping and recreating the index.

This compounds with bug 1: even after bug 1 is fixed, restarts will redo all the data loading work unnecessarily.

Fix: return parseInt(info.num_docs) > 0;

High — silent wrong behaviour

3. Anthropic variant uses the OpenAI SDK against api.anthropic.com

Both conversational_agent.js:48 and recommendation_agent.js:107 do:

this.openai = new OpenAI({ apiKey: this.llmApiKey, baseURL: this.llmBaseUrl });

For the anthropic variant, baseURL is substituted to https://api.anthropic.com/v1/, which is not OpenAI-protocol-compatible. Requests will fail at runtime. The Llama3 variant happens to work because Ollama exposes an OpenAI-compatible endpoint, but anyone selecting Anthropic in the builder gets non-working code.

Additionally, conversational_agent.js:100-104 hard-codes text-embedding-3-small and VECTOR_DIM=1536 regardless of which model the user selected. Even with a working chat-completions endpoint, embeddings require OpenAI.

Options:

  • Remove Anthropic from the JS language matrix until the templates use @anthropic-ai/sdk.
  • Or route Anthropic to its own SDK and keep embeddings on a dedicated provider.
  • Or document that JS requires an OpenAI-compatible endpoint for both chat and embeddings (and adjust the model selector accordingly).

Medium — misleading code

4. Recent-window arithmetic in conversational agent

conversational_agent.js:32 declares const RECENT_WINDOW = 6 with the comment "always include this many recent turns in context". But line 140 trims with -RECENT_WINDOW * 4 (= 24 items), and line 144 reads 0, -1 (everything in the list). Actual recent context is up to 24 messages, not 6.

The * 4 looks like leftover from an earlier design (perhaps "4 sessions × N messages"). Either drop the * 4 so behaviour matches the constant, or rename the constant to reflect the real window size.

Low — UX and consistency

5. Llama3 variant still requires LLM_API_KEY

Both templates throw if LLM_API_KEY is unset, but local Ollama doesn't need one. A user picking Llama3 has to set a dummy value, and the header line LLM_API_KEY=your_llama3_api_key reinforces the wrong expectation.

Fix: in the Llama3 variant, either skip the throw when baseUrl is localhost, or rephrase the header comment to say something like "(any non-empty value works for local Ollama)".

6. Python ↔ JS parity drift

The two languages are independent reimplementations, not ports. Implications worth deciding on (and ideally documenting on the agent builder page):

  • Conversational: Python uses RedisVL's SemanticMessageHistory; JS hand-rolls an FT index. Index names, key prefixes, field names, and retrieval strategy (pure-semantic with threshold vs. hybrid recency+KNN with no threshold) all differ. A user running both languages against the same Redis sees two parallel datasets that don't share data.
  • Recommendation: field names diverge (rating_count vs ratingCount, etc.); LLM JSON contract differs (snake_case vs camelCase, lowercase vs uppercase sort order, sentinel string vs boolean for revenue_filter); Python does no LLM-output validation while JS validates types/ranges/allowed values; Python filters genres in post-processing (because it stores the raw Python-literal blob) while JS pre-parses genres and filters server-side via TAG.
  • The JS recommendation agent is arguably better on a few axes (server-side genre filter, output validation). Worth deciding whether to back-port to Python or just call out the divergence on the docs page.

7. Generic-fallback drift

The fallback at agent-builder.js:599-640 — only reached if the static template file 404s — disagrees with the real templates:

  • Env var: fallback uses OPENAI_API_KEY / ANTHROPIC_API_KEY / LLAMA3_API_KEY; real templates use the model-agnostic LLM_API_KEY.
  • node-redis API shape: fallback uses v3-style { host, port } top-level; real templates use v4-style { socket: { host, port } }.

Not a today-bug, but a quiet rot-trap if template paths ever change.

Not bugs (avoid chasing)

  • node --check passes on all 6 generated outputs; no unresolved ${...} builder tokens leak through.
  • Imports and the npm install … comment line match in both templates.
  • node-redis v5 works with the templates even though comments imply v4 (the API surface used is preserved).
  • Server-side TAG filtering on multi-word genres ("Science Fiction") works as expected against Redis 8 — the simple regex sanitiser doesn't break them in practice.
  • The vector encoding (JSON array of floats for storage on a JSON-backed index, Buffer.from(Float32Array.buffer) for KNN query parameters) is the correct format and matches node-redis expectations.

Suggested fix order

  1. Bug 1 (dropIndex catch) — one-line change, unblocks fresh installs.
  2. Bug 2 (num_docs) — one-line change, stops needless reloads.
  3. Decide on bug 3 (Anthropic): remove from JS matrix, or properly support it.
  4. Bug 4 (recent-window) — clarify intent.
  5. Decide on bug 6 (parity vs documented divergence) — affects what the docs page should claim.
  6. Bug 5 (Llama3 key) and bug 7 (fallback drift) — cosmetic but easy.

- Fix deduplication bug: _getRecentMessages now zips Redis keys with
  json.mGet results so m._key is the actual key, not undefined
- Fix recent-window arithmetic: lTrim and lRange now use RECENT_WINDOW * 2
  (user + assistant per turn) instead of * 4
- Fix ft.dropIndex error-string match: replaced brittle catch with
  ft.info existence check to handle Redis 8 error wording
- Fix num_docs field name: ft.info returns num_docs not numDocs, so
  _indexExists was always returning false and reloading data on every start
- Fix Llama3 requiring LLM_API_KEY: default to 'no-key-needed' instead
  of throwing so local Ollama users don't need a dummy value
- Hide Anthropic from JS model selector: JS templates use the OpenAI SDK
  which is not compatible with api.anthropic.com
- Fix Jupyter button: always disabled (feature not yet available)
- Fix generic JS fallback: use LLM_API_KEY and node-redis v4 socket shape

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Comment thread static/code/agent-templates/javascript/recommendation_agent.js
Comment thread static/code/agent-templates/javascript/recommendation_agent.js Outdated
Comment thread static/js/agent-builder.js
Comment thread static/code/agent-templates/javascript/conversational_agent.js Outdated
Comment thread static/code/agent-templates/javascript/conversational_agent.js
- Fix multi-word genre TAG queries: escape spaces as \\ so 'Science Fiction'
  matches as a single token rather than two separate terms in RediSearch
- Fix reindex leaving stale data: use DD flag on ft.dropIndex to delete
  movie documents along with the index on reload
- Fix Anthropic bypass: processModelSelection now checks allowedModels so
  typing 'anthropic' or 'claude' while on JavaScript is rejected with a
  clear message rather than generating broken code
- Fix context messages out of order: sort combined recent + semantic results
  by key (which encodes timestamp) before passing to the LLM
- Fix trimmed messages never deleted: evict and delete JSON documents for
  keys that will fall off the recent window before each lTrim call

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Comment thread static/code/agent-templates/javascript/recommendation_agent.js
Comment thread static/code/agent-templates/javascript/recommendation_agent.js
Comment thread static/code/agent-templates/javascript/conversational_agent.js
- Fix hyphenated genres stripped from TAG query: allow hyphens through
  the sanitizer and backslash-escape them alongside spaces so Film-Noir
  matches the stored value instead of becoming FilmNoir
- Fix zero minRating silently dropped: use != null instead of truthy
  check so a minimum rating of 0 is included in the filter query
- Fix Llama/Ollama breaking semantic history: add separate embedder client
  (EMBEDDING_API_KEY / EMBEDDING_API_BASE_URL) that defaults to the LLM
  values so Ollama users just set EMBEDDING_MODEL=nomic-embed-text with
  no extra config, matching the pattern used in the RAG templates

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit ec2044f. Configure here.

: null,
minRating: typeof raw?.minRating === 'number' && raw.minRating >= 0 && raw.minRating <= 10
? raw.minRating
: null,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Movie rating scale mismatch

Medium Severity

The new recommendation template treats average ratings as a 0–10 scale in the LLM prompt, validation, and user-facing output, but avgRating is computed directly from MovieLens ratings_small.csv, where scores are on roughly a 0.5–5 star scale. Parsed minRating values in the 5–10 range (common when the model follows the prompt) produce Redis filters that match no documents, and results are labeled as “/10” even though values are not on that scale.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ec2044f. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants